/*
 * renamer.cc
 *
 *  Created on: Sep 28, 2012
 *      Author: niranjana
 */

#include "renamer.h"

renamer::renamer(unsigned int n_log_regs, unsigned int n_phys_regs, unsigned int n_branches)
{

	if (n_log_regs > n_phys_regs || n_branches < 1 || n_branches > 64)
		return;

	numLogicalReg = n_log_regs;
	numPhysicalReg = n_phys_regs;
	numBranches = n_branches;


	// Initialize the pointers



	activeListHead = 0;
	activeListTail = 0;
	activeListSize = numPhysicalReg - numLogicalReg;

	freeListSize = numPhysicalReg - numLogicalReg;
	freeListHead = 0;
	freeListTail = freeListSize;

	GBM = 0;

	//Allocate memory for the structures

	renameMapTable = (unsigned int *) malloc(numLogicalReg * sizeof(unsigned int));
	architecturalMapTable = (unsigned int *) malloc(numLogicalReg * sizeof(unsigned int));
	freeList = (unsigned int *) malloc (freeListSize * sizeof(unsigned int));
	activeList = (activeListEntryType *) malloc (activeListSize * sizeof(activeListEntryType));
	physicalRegisters = (unsigned long long *) malloc (numPhysicalReg * sizeof(unsigned long long));
	physicalRegisterReady = (bool *) malloc (numPhysicalReg * sizeof(bool));
	physicalRegisterTimeToGo = (int *) malloc (numPhysicalReg * sizeof(int));

	checkPoints = (branchCheckpointType *) malloc (numBranches * sizeof(branchCheckpointType));

	shadowMaps = (unsigned int *) malloc(numBranches*numLogicalReg * sizeof(unsigned int));



	for (unsigned int i = 0; i < numLogicalReg; i++)
	{
		renameMapTable[i] = i;
		architecturalMapTable[i] = i;
		physicalRegisterReady[i] = 1;
		physicalRegisterTimeToGo[i]=-1;
	}

	for (unsigned int i = 0; i < freeListSize; i++)
	{
		freeList[i] = numLogicalReg + i;
		physicalRegisterReady[numLogicalReg + i] = 0;
	}

}


renamer::~renamer()
{
}



bool renamer::stall_reg(unsigned int bundle_dst)
{
	/////////////////////////////////////////////////////////////////////
	// The Rename Stage must stall if there aren't enough free physical
	// registers available for renaming all logical destination registers
	// in the current rename bundle.
	//
	// Inputs:
	// 1. bundle_dst: number of logical destination registers in
	//    current rename bundle
	//
	// Return value:
	// Return "true" (stall) if there aren't enough free physical
	// registers to allocate to all of the logical destination registers
	// in the current rename bundle.
	/////////////////////////////////////////////////////////////////////
	bool result;
	if (freeListTail - freeListHead < freeListSize)
		result = ((freeListTail - freeListHead) < bundle_dst);
	else
		result =  false;

	return result;
}


unsigned int renamer::numCheckPoints()
{

	unsigned long long tempGBM = GBM;
	unsigned long long numCheckPoints = 0;



	for (unsigned int i = 0; i<numBranches; i++)
	{
		numCheckPoints = numCheckPoints + tempGBM % 2;
		tempGBM = tempGBM >> 1;
	}
	unsigned int result  = (unsigned int)numCheckPoints;
	return result;
}

bool renamer::stall_branch(unsigned int bundle_branch)
{


	bool result = ((numBranches - numCheckPoints()) < bundle_branch);

	return result;
}


unsigned long long renamer::get_branch_mask()
{
	return GBM;
}



unsigned int renamer::rename_rsrc(unsigned int log_reg)
{

	return renameMapTable[log_reg];
}



unsigned int renamer::rename_rdst(unsigned int log_reg)
{


	assert(!stall_reg(1));

	unsigned int renamedDest = numPhysicalReg;
	renamedDest = freeList[freeListHead%freeListSize];
	freeListHead ++;

	renameMapTable[log_reg] = renamedDest;

	return renamedDest;
}



unsigned int renamer::checkpoint()
{
	if (stall_branch(1))
		return numBranches;



	unsigned int branchID = 0;
	unsigned long long tempMask = GBM, bit64 = 1;
	while (tempMask % 2 != 0)
	{
		tempMask = tempMask >> 1;
		branchID ++;
	}

	GBM = GBM | (bit64 << branchID);


	checkPoints[branchID].checkPointFreeListHead = freeListHead;
	checkPoints[branchID].checkPointGBM = GBM;


	for(unsigned int i =0; i<numLogicalReg; i++)
		shadowMaps[branchID*numLogicalReg + i] = renameMapTable[i];

	return branchID;


}



bool renamer::stall_dispatch(unsigned int bundle_inst)
{
	bool result;

	if ((activeListTail - activeListHead) < activeListSize)
		result = (activeListSize - ((activeListTail - activeListHead)% activeListSize) < bundle_inst);
	else
		result = true;
	return result;
}



unsigned int renamer::dispatch_inst(bool dest_valid,
		unsigned int log_reg,
		unsigned int phys_reg,
		bool load,
		bool store,
		bool branch,
		unsigned int PC,
		unsigned int pe)
{


	if (stall_dispatch(1))
		return activeListSize;

	unsigned int activeListIndex = activeListTail % activeListSize;

	activeListTail ++;

	/*if (activeListTail >= 2*activeListSize)   // to stop the FIFO from growing to large numbers
	{
		activeListTail = activeListTail - activeListHead;
		activeListHead = activeListHead % activeListSize;
		activeListTail = activeListTail + activeListHead;
	}*/
	activeList[activeListIndex].destinationFlag = false;

	activeList[activeListIndex].completed = false;
	activeList[activeListIndex].exception = false;

	activeList[activeListIndex].timeBeforeCommit = -1;

	if (dest_valid == true)
	{
		activeList[activeListIndex].destinationFlag = true;
		activeList[activeListIndex].dstLogicalRegNum = log_reg;
		activeList[activeListIndex].dstPhysicalRegNum = phys_reg;
	}

	activeList[activeListIndex].isLoad = load;
	activeList[activeListIndex].isStore = store;
	activeList[activeListIndex].isbranch = branch;
	activeList[activeListIndex].programCounter = PC;
	activeList[activeListIndex].processingElementID = pe;



	return activeListIndex;
}



bool renamer::is_ready(unsigned int phys_reg)
{
	return physicalRegisterReady[phys_reg];
}



void renamer::clear_ready(unsigned int phys_reg)
{
	physicalRegisterReady[phys_reg] = false;
	physicalRegisterTimeToGo[phys_reg] = -1;
}



void renamer::set_ready(unsigned int phys_reg, unsigned int timeToGo)
{
	if (timeToGo == 0)
		physicalRegisterReady[phys_reg] = true;
	physicalRegisterTimeToGo[phys_reg] = timeToGo;
}


void renamer::updateTimeToGo()
{
	for (int i=0; i<numPhysicalReg; i++)
	{
		
		if(physicalRegisterTimeToGo[i] == 0)
			physicalRegisterReady[i] = true;
		physicalRegisterTimeToGo[i] --;	

	}

}

unsigned long long renamer::read(unsigned int phys_reg)
{
	return physicalRegisters[phys_reg];
}



void renamer::write(unsigned int phys_reg, unsigned long long value)
{
	physicalRegisters[phys_reg] = value;
	//physicalRegisterReady[phys_reg] = true;
}



void renamer::set_complete(unsigned int AL_index, unsigned int delay)
{
	activeList[AL_index].completed = true;
	activeList[AL_index].timeBeforeCommit = delay;


	// if(activeList[AL_index].destinationFlag == true)
	// physicalRegisterReady[activeList[AL_index].dstPhysicalRegNum] = true;

}



void renamer::resolve(unsigned int AL_index, unsigned int branch_ID, bool correct)
{

	unsigned long long bit64 = 1;

	if (correct == true)
	{
		GBM = GBM & ~(bit64 << branch_ID);	// Clear the GBM bit for that branch

		for (unsigned int i=0; i<numBranches; i++) // Clear the bit in all checkpoints
		{
			checkPoints[i].checkPointGBM = checkPoints[i].checkPointGBM & ~(1 << branch_ID);
		}
	}
	else
	{
		GBM = checkPoints[branch_ID].checkPointGBM;

		GBM = GBM & ~(bit64 << branch_ID);	// Clear the GBM bit for that branch

		freeListHead = checkPoints[branch_ID].checkPointFreeListHead;

		assert(freeListTail - freeListHead <= freeListSize);

		if(activeListTail % activeListSize >= AL_index + 1)
			activeListTail = 1 + activeListTail + AL_index - activeListTail % activeListSize;
		else
			activeListTail = 1 + activeListTail + AL_index - activeListTail % activeListSize - activeListSize;
		if (activeListTail - activeListHead % activeListSize !=0)
		{
			activeList[activeListTail % activeListSize].completed = false; // In case head reaches here
			activeList[activeListTail % activeListSize].exception = false; // Check if it is needed @TODO
		}

		for(unsigned int i =0; i<numLogicalReg; i++)
			renameMapTable[i] = shadowMaps[branch_ID*numLogicalReg + i];
	}
}



void renamer::commit(bool &committed, bool &load, bool &store, bool &branch, bool &exception, unsigned int &offending_PC)
{

	if (activeListTail <= activeListHead)
	{
		committed = false;
		exception = false;
		return;
	}
	unsigned int activeListIndex = activeListHead % activeListSize;

	load = activeList[activeListIndex].isLoad;
	store = activeList[activeListIndex].isStore;
	branch = activeList[activeListIndex].isbranch;

	committed = activeList[activeListIndex].completed & ~activeList[activeListIndex].exception;
	exception = activeList[activeListIndex].completed & activeList[activeListIndex].exception;

	if (committed == true)
	{
		activeListHead ++;
		if(activeList[activeListIndex].destinationFlag == true)
		{ // Add the old physical register with this logical destination to free-list. update the AMT
			assert(physicalRegisterReady[activeList[activeListIndex].dstPhysicalRegNum]);
			freeList[freeListTail%freeListSize] = architecturalMapTable[activeList[activeListIndex].dstLogicalRegNum];
			freeListTail ++;

			architecturalMapTable[activeList[activeListIndex].dstLogicalRegNum] = activeList[activeListIndex].dstPhysicalRegNum;
		}
	}


	if (exception == true)
	{
		offending_PC = activeList[activeListIndex].programCounter;

		for (unsigned int i = 0; i < numLogicalReg; i++)
		{
			renameMapTable[i] = architecturalMapTable[i];
			physicalRegisterReady[i] = 1;
			physicalRegisterTimeToGo[i]  = -1;
		}

		GBM = 0;



		activeListTail = activeListHead;

		//unsigned int temp = freeListHead;
		freeListHead = freeListTail - freeListSize;
		//freeListTail =  temp;


	}

}



void renamer::set_exception(unsigned int AL_index)
{
	activeList[AL_index].exception = true;

}

bool renamer::validateCPs()
{
	/*bool changed = false;
	unsigned int address;
	for (unsigned int i=0; i < numBranches; i++)
	{
		address = (unsigned int)&shadowMaps[i*numLogicalReg];
		changed = changed | (checkTheCPs[i] != address);
	}
	if (changed == true)
		address = 0;*/
	return false;

}


unsigned int renamer::get_pe_from_al_index(unsigned int al_index)
{
	return activeList[al_index].processingElementID;
}

void renamer::recover_exception()
{
	//Copy AMT to RMT
	for (unsigned int i = 0; i < numLogicalReg; i++)
	{
		renameMapTable[i] = architecturalMapTable[i];
		physicalRegisterReady[i] = 1;
		physicalRegisterTimeToGo[i]  = -1;
	}

	GBM = 0; //Clear GBM

	activeListTail = activeListHead; //Active list empty

	freeListHead = freeListTail - freeListSize; //Free list full
}

bool renamer::first_i_instructions_complete(unsigned int i)
{
	bool everyOneReady = true;
	for (unsigned int j=activeListHead % activeListSize ; j<= (activeListHead % activeListSize) + i; j++)
	{
		everyOneReady = everyOneReady & activeList[j].completed;
	}
	return everyOneReady;
}

bool renamer::head_trace_ready_to_retire(unsigned int trace_width)
{
	bool ready = true;
	unsigned int i, ALIndex;

	for(i = activeListHead; i<activeListTail; i++)
	{
		ALIndex = i % activeListSize;
		if(activeList[ALIndex].completed == true)
			activeList[ALIndex].timeBeforeCommit --;
	}

	for(i = 0; i<trace_width; i++)
	{
		ALIndex = (i + activeListHead) % activeListSize;
		if(activeList[ALIndex].timeBeforeCommit >= 0)
			ready = false;
	}

	return ready;
}
